package com.bsj.power.common.config.exception;

import cn.dev33.satoken.exception.*;
import com.alibaba.fastjson.JSON;
import com.bsj.power.common.config.global.JsonResult;
import com.bsj.power.common.config.global.JsonResultEnum;
import com.bsj.power.common.util.ExceptionUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.ConversionNotSupportedException;
import org.springframework.beans.TypeMismatchException;
import org.springframework.core.annotation.Order;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

/**
 * @author yinhao
 * @version 1.0
 * @description 异常拦截
 * @date 2024/4/17
 */
@Slf4j
@Order(-1)
@RestControllerAdvice
public class GlobalExceptionHandler {

    private static final String logExceptionFormat = "Capture Exception By GlobalExceptionHandler: requestUrl: %s Code: %s Detail: %s";

    /**
     * 参数校验返回
     *
     * @param ex
     * @return
     * @throws Exception
     */
    @ExceptionHandler(value = BindException.class)
    public JsonResult bindExceptionErrorHandler(BindException ex) {
        log.error("bindExceptionErrorHandler info:{}", ex.getMessage());
        StringBuilder sb = new StringBuilder();
        FieldError fieldError = ex.getFieldError();
        sb.append(fieldError.getDefaultMessage());
        return JsonResult.fail(JsonResultEnum.PARAM_ERROR, sb.toString());
    }

    /**
     * 自定义异常返回
     *
     * @param apiException
     * @return
     */
    @ExceptionHandler(ApiException.class)
    public JsonResult handFaceException(ApiException apiException) {
        JsonResultEnum status = apiException.getStatus();
        return JsonResult.fail(status);
    }

    /**
     * 其他错误
     *
     * @param ex
     * @return
     */
    @ExceptionHandler({Exception.class})
    public JsonResult exception(HttpServletRequest request, Exception ex) {
        return resultFormat(request, JsonResultEnum.EXCEPTION, ex);
    }

    /**
     * 运行时异常
     *
     * @param ex
     * @return
     */
    @ExceptionHandler(RuntimeException.class)
    public JsonResult runtimeExceptionHandler(HttpServletRequest request, RuntimeException ex) {
        return resultFormat(request, JsonResultEnum.RUNTIME_EXCEPTION, ex);
    }

    /**
     * 空指针异常
     *
     * @param ex
     * @return
     */
    @ExceptionHandler(NullPointerException.class)
    public JsonResult nullPointerExceptionHandler(HttpServletRequest request, NullPointerException ex) {
        return resultFormat(request, JsonResultEnum.NPE, ex);
    }

    /**
     * 类型转换异常
     *
     * @param ex
     * @return
     */
    @ExceptionHandler(ClassCastException.class)
    public JsonResult classCastExceptionHandler(HttpServletRequest request, ClassCastException ex) {
        return resultFormat(request, JsonResultEnum.CLASS_CAST_EXCEPTION, ex);
    }

    /**
     * IO异常
     *
     * @param ex
     * @return
     */
    @ExceptionHandler(IOException.class)
    public JsonResult iOExceptionHandler(HttpServletRequest request, IOException ex) {
        return resultFormat(request, JsonResultEnum.IO_EXCEPTION, ex);
    }

    /**
     * 未知方法异常
     *
     * @param ex
     * @return
     */
    @ExceptionHandler(NoSuchMethodException.class)
    public JsonResult noSuchMethodExceptionHandler(HttpServletRequest request, NoSuchMethodException ex) {
        return resultFormat(request, JsonResultEnum.NO_SUCH_METHOD_EXCEPTION, ex);
    }

    /**
     * 数组越界异常
     *
     * @param ex
     * @return
     */
    @ExceptionHandler(IndexOutOfBoundsException.class)
    public JsonResult indexOutOfBoundsExceptionHandler(HttpServletRequest request, IndexOutOfBoundsException ex) {
        return resultFormat(request, JsonResultEnum.INDEX_OUT_OF_BOUNDS_EXCEPTION, ex);
    }

    /**
     * 400错误
     *
     * @param ex
     * @return
     */
    @ExceptionHandler({HttpMessageNotReadableException.class})
    public JsonResult requestNotReadable(HttpServletRequest request, HttpMessageNotReadableException ex) {
        System.out.println("400..requestNotReadable");
        return resultFormat(request, JsonResultEnum.HTTP_MESSAGE_NOT_READABLE_EXCEPTION, ex);
    }

    /**
     * 400错误
     *
     * @param ex
     * @return
     */
    @ExceptionHandler({TypeMismatchException.class})
    public JsonResult requestTypeMismatch(HttpServletRequest request, TypeMismatchException ex) {
        System.out.println("400..TypeMismatchException");
        return resultFormat(request, JsonResultEnum.TYPE_MISMATCH_EXCEPTION, ex);
    }

    /**
     * 400错误
     *
     * @param ex
     * @return
     */
    @ExceptionHandler({MissingServletRequestParameterException.class})
    public JsonResult requestMissingServletRequest(HttpServletRequest request, MissingServletRequestParameterException ex) {
        System.out.println("400..MissingServletRequest");
        return resultFormat(request, JsonResultEnum.MISSING_SERVLET_REQUEST_PARAMETER_EXCEPTION, ex);
    }

    /**
     * 405错误
     *
     * @param ex
     * @return
     */
    @ExceptionHandler({HttpRequestMethodNotSupportedException.class})
    public JsonResult request405(HttpServletRequest request, HttpRequestMethodNotSupportedException ex) {
        return resultFormat1(request, JsonResultEnum.HTTP_REQUEST_METHOD_NOT_SUPPORTED_EXCEPTION, ex);
    }


    /**
     * 500错误
     *
     * @param ex
     * @return
     */
    @ExceptionHandler({ConversionNotSupportedException.class, HttpMessageNotWritableException.class})
    public JsonResult server500(HttpServletRequest request, RuntimeException ex) {
        System.out.println("500...");
        return resultFormat(request, JsonResultEnum.FAIL_SERVER, ex);
    }

    /**
     * 栈溢出
     *
     * @param ex
     * @return
     */
    @ExceptionHandler({StackOverflowError.class})
    public JsonResult requestStackOverflow(HttpServletRequest request, StackOverflowError ex) {
        return resultFormat(request, JsonResultEnum.SOE, ex);
    }

    /**
     * Sa-Token权限异常类
     * @param ex
     * @return
     */
    @ExceptionHandler({SaTokenException.class})
    public void requestAuth(HttpServletResponse response, SaTokenException ex) throws IOException{
        JsonResult jsonResult = null;
        Integer status = null;
        if (ex instanceof NotLoginException) {
            NotLoginException notLoginException = (NotLoginException) ex;
            log.warn("Sa-Token NotLoginException: {}", notLoginException.getMessage());
            status = JsonResultEnum.LOGIN_EXIT.getCode();
            jsonResult = JsonResult.fail(JsonResultEnum.LOGIN_EXIT);
        } else if (ex instanceof NotRoleException) {
            NotRoleException notRoleException = (NotRoleException) ex;
            log.warn("Sa-Token NotRoleException: {}", notRoleException.getMessage());
            jsonResult = JsonResult.fail(JsonResultEnum.ROLE_EXCEPTION);
        } else if (ex instanceof NotPermissionException) {
            NotPermissionException notPermissionException = (NotPermissionException) ex;
            log.warn("Sa-Token NotPermissionException: {}", notPermissionException.getMessage());
            jsonResult = JsonResult.fail(JsonResultEnum.PERMISSION_EXCEPTION);
        } else {
            DisableServiceException disableServiceException = (DisableServiceException) ex;
            log.warn("Sa-Token DisableServiceException: {}", disableServiceException.getMessage());
            jsonResult = JsonResult.fail(JsonResultEnum.USER_DISABLE);
        }
        returnAuthInvalid(response, status, jsonResult);
    }


    private <T extends Throwable> JsonResult resultFormat(HttpServletRequest request, JsonResultEnum jsonResultEnum, T ex) {
        log.error("[全局服务异常]: {}", ExceptionUtil.getStackStr(ex));
        log.error("@@@服务异常" + String.format(logExceptionFormat, request.getRequestURL(), jsonResultEnum, ExceptionUtil.getStackStr(ex)));
        return JsonResult.fail(JsonResultEnum.EXCEPTION);
    }

    private <T extends Throwable> JsonResult resultFormat1(HttpServletRequest request, JsonResultEnum jsonResultEnum, T ex) {
        log.error("[全局服务异常]: {}", ExceptionUtil.getStackStr(ex));
        log.error("@@@服务异常" + String.format(logExceptionFormat, request.getRequestURL(), jsonResultEnum, ExceptionUtil.getStackStr(ex)));
        return JsonResult.fail(jsonResultEnum);
    }

    public void returnAuthInvalid(HttpServletResponse response, Integer status, JsonResult msg) throws IOException {
        response.setContentType(MediaType.APPLICATION_JSON_VALUE);
        response.setCharacterEncoding("UTF-8");
        if (status != null) {
            response.setStatus(status);
        } else {
            response.setStatus(200);
        }
        PrintWriter out = response.getWriter();
        out.print(JSON.toJSONString(msg));
    }
}
